package com.googlecode.economini.issues;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.util.Log;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.googlecode.economini.converter.IssueListConverter;
import com.googlecode.economini.converter.IssueListToken;
import com.googlecode.economini.converter.Token;
import com.googlecode.economini.issues.task.LoadIssueTask;

public class IssueManager extends Binder {

  private static final String LOGGING_TAG = "IssueManager";

  /** Immutable map from token types to column names */
  private static final Map<IssueListToken, String> TOKEN_MAPPING;

  static {
    Map<IssueListToken, String> map = Maps.newEnumMap(IssueListToken.class);
    map.put(IssueListToken.COVER_URL, IssueStore.COVER_URL_COLUMN);
    map.put(IssueListToken.DATE, IssueStore.TITLE_COLUMN);
    map.put(IssueListToken.URL, IssueStore.URL_COLUMN);
    TOKEN_MAPPING = Collections.unmodifiableMap(map);
  }

  private static final String INDEX_URL = "http://www.economist.com/printedition/covers";
//  private static final String INDEX_URL = "file:///sdcard/test/issues.htm";

  private final ListeningExecutorService executor;
  private final IssueStore issueSummaryStore;
  private final ContentResolver resolver;

  public IssueManager(
      ListeningExecutorService executor,
      IssueStore issueStore,
      ContentResolver resolver) {
    this.executor = executor;
    this.issueSummaryStore = issueStore;
    this.resolver = resolver;
  }

  public void refreshIssues() {
    Callable<Object> refreshTask = new Callable<Object>() {
      @Override
      public Object call() throws Exception {
        BufferedInputStream inputStream = null;
        try {
          IssueListConverter converter = IssueListConverter.newInstance();

          URLConnection connection = new URL(INDEX_URL).openConnection();
          inputStream = new BufferedInputStream(connection.getInputStream());

          List<Token<IssueListToken>> tokens = converter
              .parse(new InputStreamReader(inputStream));

          storeIssueDescription(tokens);
          return null;
        } catch (IOException e) {
          throw new RuntimeException("Unable to connect", e);
        } finally {
          if (inputStream != null) {
            inputStream.close();
          }
        }
      }

    };

    Future<Object> future = executor.submit(refreshTask);
    try {
      future.get();
    } catch (InterruptedException e) {
      Log.e(LOGGING_TAG, "Interrupted loading issues");
    } catch (ExecutionException e) {
      Log.e(LOGGING_TAG, "Error loading issues", e.getCause());
    }
  }

  public void loadIssue(long issueId) {
    Log.i("IssueManager", "Loading issue " + issueId);
    executor.submit(new LoadIssueTask(executor, resolver, issueId));
  }

  private void storeIssueDescription(List<Token<IssueListToken>> tokens) {
    SQLiteDatabase db = issueSummaryStore.getWritableDatabase();

    ContentValues values = new ContentValues();
    for (Token<IssueListToken> token : tokens) {
      String columnName = TOKEN_MAPPING.get(token.getType());

      if (values.containsKey(columnName)) {
        // If column is present, store the current values
        long result = db.insertWithOnConflict(
            IssueStore.ISSUE_TABLE,
            null,
            values,
            SQLiteDatabase.CONFLICT_IGNORE);
        if (result == -1) {
          Log.w(LOGGING_TAG, "Error storing issue");
        }
        values.clear();
      }

      values.put(columnName, token.getValue());
    }

    // Write out any remaining values
    if (values.size() > 0) {
      long result = db.insertWithOnConflict(
          IssueStore.ISSUE_TABLE,
          null,
          values,
          SQLiteDatabase.CONFLICT_IGNORE);
      if (result == -1) {
        Log.w(LOGGING_TAG, "Error storing issue");
      }
    }

    db.close();
  }
}
